package me.riddhimanadib.formmaster.adapter;

import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.support.annotation.IdRes;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.AppCompatEditText;
import android.support.v7.widget.AppCompatRadioButton;
import android.support.v7.widget.AppCompatTextView;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SwitchCompat;
import android.text.Editable;
import android.text.InputType;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.CompoundButton;
import android.widget.ImageButton;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import me.riddhimanadib.formmaster.R;
import me.riddhimanadib.formmaster.helper.DateTimePickerDialog;
import me.riddhimanadib.formmaster.helper.SingleFilterDialog;
import me.riddhimanadib.formmaster.listener.OnFormElementValueChangedListener;
import me.riddhimanadib.formmaster.model.FormElement;
import me.riddhimanadib.formmaster.model.FormHeader;
import me.riddhimanadib.formmaster.model.FormObject;

/**
 * The adpater the holds and displays the form objects
 * Created by Adib on 16-Apr-17.
 */

public class FormAdapter extends RecyclerView.Adapter<FormAdapter.FormViewHolder> {

    // defining marker for header view
    private int IS_HEADER_VIEW = 0;

    private List<FormObject> mDataset;
    private Context mContext;
    private Calendar mCalendarCurrentDate;
    private Calendar mCalendarCurrentTime;
    private Calendar mCalendarCurrentDateTime;
    private OnFormElementValueChangedListener mListener;
    private int[] formRadioButtonIds = new int[]{R.id.fromElementRB0, R.id.fromElementRB1, R.id.fromElementRB2,};

    private int clickedPosition;

    private final ForegroundColorSpan RED_SPAN = new ForegroundColorSpan(Color.RED);

    private boolean editable = true;

    /**
     * public constructor with context
     *
     * @param context
     */
    public FormAdapter(Context context, OnFormElementValueChangedListener listener) {
        mContext = context;
        mListener = listener;
        mDataset = new ArrayList<>();
        mCalendarCurrentDate = Calendar.getInstance();
        mCalendarCurrentTime = Calendar.getInstance();
        mCalendarCurrentDateTime = Calendar.getInstance();
        clickedPosition = -1;
    }

    /**
     * adds list of elements to be shown
     *
     * @param formObjects
     */
    public void addElements(List<FormObject> formObjects) {
        this.mDataset = formObjects;
    }

    /**
     * adds single element to be shown
     *
     * @param formObject
     */
    public void addElement(FormObject formObject) {
        this.mDataset.add(formObject);
    }

    /**
     * set value for any unique index
     *
     * @param position
     * @param value
     */
    public void setValueAtIndex(int position, String value) {
        FormElement formElement = (FormElement) mDataset.get(position);
        formElement.setValue(value);
    }

    /**
     * set value for any unique tag
     *
     * @param tag
     * @param value
     */
    public void setValueAtTag(int tag, String value) {

        for (FormObject f : this.mDataset) {
            if (f instanceof FormElement) {
                FormElement formElement = (FormElement) f;
                if (formElement.getTag() == tag) {
                    formElement.setValue(value);
                    return;
                }
            }
        }
    }

    /**
     * get value of any element by tag
     *
     * @param index
     * @return
     */
    public FormObject getValueAtIndex(int index) {
        return (mDataset.get(index));
    }

    /**
     * get value of any element by tag
     *
     * @param tag
     * @return
     */
    public FormElement getValueAtTag(int tag) {
        for (FormObject f : this.mDataset) {
            if (f instanceof FormElement) {
                FormElement formElement = (FormElement) f;
                if (formElement.getTag() == tag) {
                    return formElement;
                }
            }
        }

        return null;
    }

    public void setEditable(boolean editable) {
        this.editable = editable;
    }

    @Override
    public int getItemCount() {
        return mDataset.size();
    }

    @Override
    public int getItemViewType(int position) {
        if (mDataset.get(position).isHeader()) {
            return IS_HEADER_VIEW;
        } else {
            return ((FormElement) mDataset.get(position)).getType();
        }
    }

    @Override
    public FormAdapter.FormViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // FIXME: 17/10/18 在这里添加ViewHolder
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View v;
        FormAdapter.FormViewHolder vh;
        switch (viewType) {
            case 0:
                v = inflater.inflate(R.layout.form_element_header, parent, false);
                vh = new HeaderViewHolder(v);
                break;
            case FormElement.TYPE_SWITCH:
                v = inflater.inflate(R.layout.form_element_switch, parent, false);
                vh = new SwitchViewHolder(v, new FormCompoundButtonListener());
                break;
            case FormElement.TYPE_RADIOGROUP:
                v = inflater.inflate(R.layout.form_element_radio_group, parent, false);
                vh = new RadioGroupViewHolder(v, new FormRadioGroupListener());
                break;
            case FormElement.TYPE_EDITTEXT_ASSISTANT_BUTTON:
                v = inflater.inflate(R.layout.form_element_assistant_button, parent, false);
                vh = new AssistantButtonViewHolder(v, new FormCustomEditTextListener());
                break;
            case FormElement.TYPE_EMPTY:
                v = inflater.inflate(R.layout.form_element_empty, parent, false);
                vh = new EmptyViewHolder(v);
                break;
            default:
                v = inflater.inflate(R.layout.form_element, parent, false);
                vh = new EditTextViewHolder(v, new FormCustomEditTextListener());
                break;
        }
        return vh;
    }

    @Override
    public void onBindViewHolder(FormAdapter.FormViewHolder vh, final int position) {

        // updates edit text listener index
        vh.callback.updatePosition(vh.getAdapterPosition());

        // gets current object
        FormObject currentObject = mDataset.get(position);
        // FIXME: 17/10/18 根据不同类型进行绑定
        int itemType = getItemViewType(position);
        if (itemType == IS_HEADER_VIEW) {
            FormHeader formHeader = (FormHeader) currentObject;
            ((HeaderViewHolder) vh).mTextViewTitle.setText(formHeader.getTitle());
        } else if (itemType == FormElement.TYPE_SWITCH) {
            SwitchViewHolder holder = (SwitchViewHolder) vh;
            FormElement formElement = (FormElement) currentObject;
            setupTitle(holder.mTextViewTitle, formElement);
            setupDivider(holder.mDivider, position);
            boolean isChecked = formElement.getValue() != null && formElement.getValue().equals(formElement.getCheckedAndNotCheckValue().first);
            holder.mSwitchValue.setChecked(isChecked);
            holder.mSwitchValue.setClickable(editable);
        } else if (itemType == FormElement.TYPE_RADIOGROUP) {
            RadioGroupViewHolder holder = (RadioGroupViewHolder) vh;
            FormElement formElement = (FormElement) currentObject;
            String currentValue = formElement.getValue();
            List<String> options = formElement.getOptions();
            if (options == null || options.size() < 2 || options.size() > 3) {
                throw new RuntimeException("RadioGroup的类型需要Options的数目为2-3个");
            }
            int checkId = -1;
            setupTitle(holder.mTextViewTitle, formElement);
            setupDivider(holder.mDivider, position);
            for (int index = 0; index < 3; index++) {
                AppCompatRadioButton rb = holder.mRadioButtons[index];
                if (index < options.size()) {
                    String value = options.get(index);
                    rb.setVisibility(View.VISIBLE);
                    rb.setText(value);
                    if (value.equals(currentValue)) checkId = formRadioButtonIds[index];
                } else {
                    rb.setVisibility(View.GONE);
                }
                rb.setClickable(editable);
            }
            if (checkId >= 0) holder.mRadioValue.check(checkId);
        } else if (itemType == FormElement.TYPE_EDITTEXT_ASSISTANT_BUTTON) {
            final AssistantButtonViewHolder holder = (AssistantButtonViewHolder) vh;
            // other wise, it just displays form element with respective type
            final FormElement formElement = (FormElement) currentObject;
            setupTitle(holder.mTextViewTitle, formElement);
            setupDivider(holder.mDivider, position);
            holder.mEditTextValue.setText(formElement.getValue());
            holder.mEditTextValue.setHint(formElement.getHint());
            holder.mEditTextValue.setSingleLine(false);
            holder.mEditTextValue.setMaxLines(4);
            holder.itemView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    holder.mEditTextValue.requestFocus();
                    InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
                    imm.showSoftInput(holder.mEditTextValue, InputMethodManager.SHOW_IMPLICIT);
                }
            });
            holder.btnAssistant.setImageResource(formElement.getAssistantButtonResId());
            holder.btnAssistant.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    FormElement.FormAssistantCallback callback = formElement.getFormAssistantCallback();
                    if (callback != null) callback.onAssistantButtonClick(holder.mEditTextValue);
                }
            });
            holder.btnAssistant.setClickable(editable);
            holder.mEditTextValue.setFocusable(false);
        } else if (itemType == FormElement.TYPE_EMPTY) {
            FormElement formElement = (FormElement) currentObject;
            EmptyViewHolder holder = (EmptyViewHolder) vh;
            String title = formElement.getTitle();
            if (TextUtils.isEmpty(title)) {
                holder.mTextViewTitle.setText(R.string.no_data);
            } else {
                holder.mTextViewTitle.setText(title);
            }
        } else {
            EditTextViewHolder holder = (EditTextViewHolder) vh;
            // other wise, it just displays form element with respective type
            FormElement formElement = (FormElement) currentObject;
            setupTitle(holder.mTextViewTitle, formElement);
            setupDivider(holder.mDivider, position);
            holder.mEditTextValue.setText(formElement.getValue());
            holder.mEditTextValue.setHint(formElement.getHint());
            switch (getItemViewType(position)) {
                case FormElement.TYPE_EDITTEXT_TEXT_SINGLELINE:
                    holder.mEditTextValue.setMaxLines(1);
                    setEditTextFocusEnabled(holder);
                    break;
                case FormElement.TYPE_EDITTEXT_TEXT_MULTILINE:
                    holder.mEditTextValue.setSingleLine(false);
                    holder.mEditTextValue.setMaxLines(4);
                    setEditTextFocusEnabled(holder);
                    break;
                case FormElement.TYPE_EDITTEXT_NUMBER:
                    holder.mEditTextValue.setRawInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
                    setEditTextFocusEnabled(holder);
                    break;
                case FormElement.TYPE_EDITTEXT_EMAIL:
                    holder.mEditTextValue.setRawInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
                    setEditTextFocusEnabled(holder);
                    break;
                case FormElement.TYPE_EDITTEXT_PHONE:
                    holder.mEditTextValue.setRawInputType(InputType.TYPE_CLASS_PHONE | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
                    setEditTextFocusEnabled(holder);
                    break;
                case FormElement.TYPE_EDITTEXT_PASSWORD:
                    holder.mEditTextValue.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
                    holder.mEditTextValue.setSelection(holder.mEditTextValue.getText().length());
                    setEditTextFocusEnabled(holder);
                    break;
                case FormElement.TYPE_PICKER_DATE:
                    setDatePicker(holder, position);
                    break;
                case FormElement.TYPE_PICKER_TIME:
                    setTimePicker(holder, position);
                    break;
                case FormElement.TYPE_PICKER_DATE_AND_TIME:
                    setDateTimePicker(holder, position);
                    break;
                case FormElement.TYPE_PICKER_SINGLE:
                    holder.mEditTextValue.setSingleLine(false);
                    setSingleOptions(holder, position);
                    break;
                case FormElement.TYPE_PICKER_SINGLE_WITH_FILTER:
                    holder.mEditTextValue.setSingleLine(false);
                    setSingleOptionsWithFilter(holder, position);
                    break;
                case FormElement.TYPE_PICKER_MULTI_CHECKBOX:
                    holder.mEditTextValue.setSingleLine(false);
                    setMultipleOptions(holder, position);
                    break;
                default:
                    break;
            }
            holder.mEditTextValue.setFocusable(editable);
            holder.mEditTextValue.setClickable(editable);
            holder.itemView.setClickable(editable);
        }
    }

    private void setupTitle(TextView tv, FormElement e) {
        SpannableStringBuilder ssb = new SpannableStringBuilder(e.getTitle());
        if (e.isRequired()) {
            ssb.append(" *");
            ssb.setSpan(RED_SPAN, ssb.length() - 1, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
        tv.setText(ssb, TextView.BufferType.SPANNABLE);
    }

    private void setupDivider(View mDivider, int position) {
        if (position == 0) {
            mDivider.setVisibility(View.INVISIBLE);
        } else if (getItemViewType(position - 1) == IS_HEADER_VIEW) {
            mDivider.setVisibility(View.INVISIBLE);
        } else mDivider.setVisibility(View.VISIBLE);
    }

    /**
     * brings focus when clicked on the whole container
     *
     * @param holder
     */
    private void setEditTextFocusEnabled(final EditTextViewHolder holder) {
        holder.itemView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                holder.mEditTextValue.requestFocus();
                InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.showSoftInput(holder.mEditTextValue, InputMethodManager.SHOW_IMPLICIT);
            }
        });
    }

    private void setDatePicker(EditTextViewHolder holder, final int position) {

        holder.mEditTextValue.setFocusableInTouchMode(false);
        holder.mEditTextValue.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // saves clicked position for further reference
                showDatePickerDialog(position);
            }
        });

        holder.itemView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // saves clicked position for further reference
                showDatePickerDialog(position);
            }
        });

    }

    private void showDatePickerDialog(int position) {
        if (!editable) return;
        clickedPosition = position;
        DateTimePickerDialog datePickerDialog = new DateTimePickerDialog(mContext, true, DateTimePickerDialog.TYPE_DATE_PICKER).setSelectedDate(
                mCalendarCurrentDate).setUse24Hour(true).setCallback(dateCallback);
        datePickerDialog.show();
    }

    private void setTimePicker(EditTextViewHolder holder, final int position) {

        holder.mEditTextValue.setFocusableInTouchMode(false);
        holder.mEditTextValue.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                showTimePicker(position);
            }
        });

        holder.itemView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                showTimePicker(position);
            }
        });

    }

    private void showTimePicker(int position) {
        if (!editable) return;
        clickedPosition = position;
        DateTimePickerDialog timePickerDialog = new DateTimePickerDialog(mContext, true, DateTimePickerDialog.TYPE_TIME_PICKER).setSelectedDate(
                mCalendarCurrentTime).setUse24Hour(true).setCallback(timeCallback);
        timePickerDialog.show();
    }

    private void setDateTimePicker(EditTextViewHolder holder, final int position) {

        holder.mEditTextValue.setFocusableInTouchMode(false);
        holder.mEditTextValue.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // saves clicked position for further reference
                showDateTimePickerDialog(position);
            }
        });

        holder.itemView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // saves clicked position for further reference
                showDateTimePickerDialog(position);
            }
        });

    }

    private void showDateTimePickerDialog(int position) {
        if (!editable) return;
        clickedPosition = position;
        DateTimePickerDialog dateTimePickerDialog = new DateTimePickerDialog(mContext, true).setSelectedDate(mCalendarCurrentDateTime)
                                                                                            .setUse24Hour(true)
                                                                                            .setCallback(datetimeCallback);
        dateTimePickerDialog.show();
    }

    /**
     * prepares a single picker dialog for the clicked position and attaches click listener for the passed edittext
     *
     * @param holder
     * @param position
     */
    private void setSingleOptions(final EditTextViewHolder holder, final int position) {

        // get the element
        final FormElement currentObj = (FormElement) mDataset.get(position);

        holder.mEditTextValue.setFocusableInTouchMode(false);
        // reformat the options in format needed
        final CharSequence[] options = new CharSequence[currentObj.getOptions().size()];
        for (int i = 0; i < currentObj.getOptions().size(); i++) {
            options[i] = currentObj.getOptions().get(i);
        }

        if (!editable) return;

        // prepare the dialog
        final AlertDialog dialog = new AlertDialog.Builder(mContext).setTitle(mContext.getString(R.string.please_select_item, currentObj.getTitle()))

                                                                    .setItems(options, new DialogInterface.OnClickListener() {
                                                                        public void onClick(DialogInterface dialog, int which) {
                                                                            holder.mEditTextValue.setText(options[which]);
                                                                            currentObj.setValue(options[which].toString());
                                                                            notifyDataSetChanged();
                                                                        }
                                                                    }).create();
        holder.mEditTextValue.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // saves clicked position for further reference
                dialog.show();
            }
        });
        // display the dialog on click
        holder.itemView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                dialog.show();
            }
        });

    }

    /**
     * 带筛选框的单项选择
     */
    private void setSingleOptionsWithFilter(final EditTextViewHolder holder, final int position) {

        // get the element
        final FormElement currentObj = (FormElement) mDataset.get(position);

        holder.mEditTextValue.setFocusableInTouchMode(false);

        if (!editable) return;
        final SingleFilterDialog dialog = new SingleFilterDialog(mContext, currentObj.getOptions());
        dialog.setTitle(mContext.getString(R.string.please_select_item, currentObj.getTitle()));
        dialog.setFilterKeys(currentObj.getFilterKeys());
        dialog.setCallback(new SingleFilterDialog.Callback() {
            @Override
            public void onItemSelected(String value) {
                holder.mEditTextValue.setText(value);
                currentObj.setValue(value);
                notifyDataSetChanged();
            }
        });
        holder.mEditTextValue.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.show();
            }
        });
        // display the dialog on click
        holder.itemView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                dialog.show();
            }
        });

    }

    /**
     * prepares a multi checkbox picker dialog for the clicked position and attaches click listener for the passed edittext
     *
     * @param holder
     * @param position
     */
    private void setMultipleOptions(final EditTextViewHolder holder, final int position) {

        holder.mEditTextValue.setFocusableInTouchMode(false);

        holder.mEditTextValue.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // saves clicked position for further reference
                showMultiOptionsDialog(position, v);
            }
        });
        // display the dialog on click
        holder.itemView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                showMultiOptionsDialog(position, view.findViewById(R.id.formElementValue));
            }
        });
    }

    private void showMultiOptionsDialog(final int position, final View valueView) {
        // get the element
        final FormElement currentObj = (FormElement) mDataset.get(position);

        // reformat the options in format needed
        final String[] options = new String[currentObj.getOptions().size()];
        final boolean[] optionsSelected = new boolean[currentObj.getOptions().size()];
        final ArrayList<Integer> mSelectedItems = new ArrayList<>();

        for (int i = 0; i < currentObj.getOptions().size(); i++) {
            options[i] = currentObj.getOptions().get(i);
            optionsSelected[i] = false;

            if (currentObj.getOptionsSelected().contains(options[i])) {
                optionsSelected[i] = true;
                mSelectedItems.add(i);
            }
        }

        if (!editable) return;

        // prepare the dialog
        AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
        builder.setTitle(mContext.getString(R.string.please_select_item, currentObj.getTitle()))
               .setMultiChoiceItems(options, optionsSelected, new DialogInterface.OnMultiChoiceClickListener() {
                   @Override
                   public void onClick(DialogInterface dialog, int which, boolean isChecked) {
                       if (isChecked) {
                           // If the user checked the item, add it to the selected items
                           mSelectedItems.add(which);
                       } else if (mSelectedItems.contains(which)) {
                           // Else, if the item is already in the array, remove it
                           mSelectedItems.remove(Integer.valueOf(which));
                       }
                   }
               })
               // Set the action buttons
               .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                   @Override
                   public void onClick(DialogInterface dialog, int id) {
                       String s = "";
                       ArrayList<String> mSelectedItemList = new ArrayList<>();
                       for (int i = 0; i < mSelectedItems.size(); i++) {
                           String value = options[mSelectedItems.get(i)];
                           s += value;
                           if (i < mSelectedItems.size() - 1) {
                               s += ", ";
                           }
                           mSelectedItemList.add(value);
                       }

                       ((TextView) valueView).setText(s);
                       FormElement element = (FormElement) mDataset.get(position);
                       element.setValue(s);
                       element.setOptionsSelected(mSelectedItemList);
                       notifyDataSetChanged();
                   }
               })
               .setNegativeButton(android.R.string.cancel, null);
        builder.show();
    }

    public static class FormViewHolder extends RecyclerView.ViewHolder {
        public FormValueCallback callback;

        public FormViewHolder(View itemView, FormValueCallback callback) {
            super(itemView);
            this.callback = callback;
        }
    }

    /**
     * EditText的ViewHolder
     */
    public static class EditTextViewHolder extends FormViewHolder {

        public AppCompatTextView mTextViewTitle;
        public AppCompatTextView mTextViewOptions;
        public AppCompatEditText mEditTextValue;
        public View mDivider;

        public EditTextViewHolder(View v, FormCustomEditTextListener listener) {
            super(v, listener);
            mDivider = v.findViewById(R.id.mDivider);
            mTextViewTitle = (AppCompatTextView) v.findViewById(R.id.formElementTitle);
            mTextViewOptions = (AppCompatTextView) v.findViewById(R.id.formElementTitle);
            mEditTextValue = (AppCompatEditText) v.findViewById(R.id.formElementValue);
            if (mEditTextValue != null) mEditTextValue.addTextChangedListener(listener);
        }
    }

    /**
     * Switch的ViewHolder
     */
    public static class SwitchViewHolder extends FormViewHolder {

        public AppCompatTextView mTextViewTitle;
        public AppCompatTextView mTextViewOptions;
        public SwitchCompat mSwitchValue;
        public View mDivider;

        public SwitchViewHolder(View v, FormCompoundButtonListener listener) {
            super(v, listener);
            mDivider = v.findViewById(R.id.mDivider);
            mTextViewTitle = (AppCompatTextView) v.findViewById(R.id.formElementTitle);
            mTextViewOptions = (AppCompatTextView) v.findViewById(R.id.formElementTitle);
            mSwitchValue = (SwitchCompat) v.findViewById(R.id.formElementValue);
            if (mSwitchValue != null) mSwitchValue.setOnCheckedChangeListener(listener);
        }
    }

    /**
     * RadioGroup的ViewHolder
     */
    public static class RadioGroupViewHolder extends FormViewHolder {

        public AppCompatTextView mTextViewTitle;
        public AppCompatTextView mTextViewOptions;
        public RadioGroup mRadioValue;
        public AppCompatRadioButton[] mRadioButtons;
        public AppCompatRadioButton mRadioButton0;
        public AppCompatRadioButton mRadioButton1;
        public AppCompatRadioButton mRadioButton2;
        public View mDivider;

        public RadioGroupViewHolder(View v, FormRadioGroupListener listener) {
            super(v, listener);
            mDivider = v.findViewById(R.id.mDivider);
            mTextViewTitle = (AppCompatTextView) v.findViewById(R.id.formElementTitle);
            mTextViewOptions = (AppCompatTextView) v.findViewById(R.id.formElementTitle);
            mRadioValue = (RadioGroup) v.findViewById(R.id.formElementValue);
            mRadioButton0 = (AppCompatRadioButton) v.findViewById(R.id.fromElementRB0);
            mRadioButton1 = (AppCompatRadioButton) v.findViewById(R.id.fromElementRB1);
            mRadioButton2 = (AppCompatRadioButton) v.findViewById(R.id.fromElementRB2);
            mRadioButtons = new AppCompatRadioButton[]{mRadioButton0, mRadioButton1, mRadioButton2};
            if (mRadioValue != null) mRadioValue.setOnCheckedChangeListener(listener);
        }

    }

    /**
     * 头ViewHolder
     */
    public static class HeaderViewHolder extends FormViewHolder {

        public AppCompatTextView mTextViewTitle;

        public HeaderViewHolder(View v) {
            super(v, new FormValueCallback() {
                @Override
                public void updatePosition(int position) {

                }
            });
            mTextViewTitle = (AppCompatTextView) v.findViewById(R.id.formElementTitle);
        }
    }

    /**
     * 带辅助按键的ViewHolder
     */
    private class AssistantButtonViewHolder extends FormViewHolder {

        public View mDivider;
        public AppCompatTextView mTextViewTitle;
        public ImageButton btnAssistant;
        public AppCompatEditText mEditTextValue;

        public AssistantButtonViewHolder(View v, FormCustomEditTextListener listener) {
            super(v, listener);
            mDivider = (View) v.findViewById(R.id.mDivider);
            mTextViewTitle = (AppCompatTextView) v.findViewById(R.id.formElementTitle);
            btnAssistant = (ImageButton) v.findViewById(R.id.btnAssistant);
            mEditTextValue = (AppCompatEditText) v.findViewById(R.id.formElementValue);
            if (mEditTextValue != null) mEditTextValue.addTextChangedListener(listener);
        }
    }

    /**
     * 空ViewHolder
     */
    public static class EmptyViewHolder extends FormViewHolder {

        public AppCompatTextView mTextViewTitle;

        public EmptyViewHolder(View v) {
            super(v, new FormValueCallback() {
                @Override
                public void updatePosition(int position) {

                }
            });
            mTextViewTitle = (AppCompatTextView) v.findViewById(R.id.formElementTitle);
        }
    }

    // FIXME: 17/10/18 添加Viewholder

    private interface FormValueCallback {
        void updatePosition(int position);
    }

    /**
     * Text watcher for Edit texts
     */
    private class FormCustomEditTextListener implements TextWatcher, FormValueCallback {
        private int position;

        @Override
        public void updatePosition(int position) {
            this.position = position;
        }

        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {

        }

        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {

            // get current form element, existing value and new value
            FormElement formElement = (FormElement) mDataset.get(position);
            String currentValue = formElement.getValue();
            String newValue = charSequence.toString();

            // trigger event only if the value is changed
            if (!currentValue.equals(newValue)) {
                formElement.setValue(newValue);
                if (mListener != null) mListener.onValueChanged(formElement);
            }

        }

        @Override
        public void afterTextChanged(Editable editable) {

        }
    }

    private class FormCompoundButtonListener implements CompoundButton.OnCheckedChangeListener, FormValueCallback {

        private int position;

        @Override
        public void updatePosition(int position) {
            this.position = position;
        }

        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if (buttonView.isPressed()) {
                FormElement formElement = (FormElement) mDataset.get(position);
                String newValue = isChecked ? formElement.getCheckedAndNotCheckValue().first : formElement.getCheckedAndNotCheckValue().second;
                formElement.setValue(newValue);
                if (mListener != null) mListener.onValueChanged(formElement);
            }
        }

    }

    private class FormRadioGroupListener implements RadioGroup.OnCheckedChangeListener, FormValueCallback {

        private int position;

        @Override
        public void updatePosition(int position) {
            this.position = position;
        }

        @Override
        public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
            RadioButton button = (RadioButton) group.findViewById(checkedId);
            FormElement formElement = (FormElement) mDataset.get(position);
            String currentValue = formElement.getValue();
            String newValue = button.getText().toString();
            if (!currentValue.equals(newValue)) {
                formElement.setValue(newValue);
                if (mListener != null) mListener.onValueChanged(formElement);
            }
        }
    }

    // FIXME: 17/10/18 添加监听器

    private DateTimePickerDialog.Callback dateCallback = new DateTimePickerDialog.Callback() {
        @Override
        public void onDateTimeSet(Calendar ca, Date date, Long millis, String text) {
            mCalendarCurrentDate.setTimeInMillis(millis);
            if (clickedPosition >= 0) {
                FormElement formElement = (FormElement) mDataset.get(clickedPosition);
                String currentValue = formElement.getValue();
                if (!text.equals(currentValue)) {
                    formElement.setValue(text);
                    notifyDataSetChanged();
                    if (mListener != null) mListener.onValueChanged(formElement);
                }
                clickedPosition = -1;
            }
        }

        @Override
        public void onCancelled() {

        }
    };

    private DateTimePickerDialog.Callback timeCallback = new DateTimePickerDialog.Callback() {
        @Override
        public void onDateTimeSet(Calendar ca, Date date, Long millis, String text) {
            mCalendarCurrentTime.setTimeInMillis(millis);
            if (clickedPosition >= 0) {
                FormElement formElement = (FormElement) mDataset.get(clickedPosition);
                String currentValue = formElement.getValue();
                if (!text.equals(currentValue)) {
                    formElement.setValue(text);
                    notifyDataSetChanged();
                    if (mListener != null) mListener.onValueChanged(formElement);
                }
                clickedPosition = -1;
            }
        }

        @Override
        public void onCancelled() {

        }
    };

    private DateTimePickerDialog.Callback datetimeCallback = new DateTimePickerDialog.Callback() {
        @Override
        public void onDateTimeSet(Calendar ca, Date date, Long millis, String text) {
            mCalendarCurrentDateTime.setTimeInMillis(millis);
            if (clickedPosition >= 0) {
                FormElement formElement = (FormElement) mDataset.get(clickedPosition);
                String currentValue = formElement.getValue();
                if (!text.equals(currentValue)) {
                    formElement.setValue(text);
                    notifyDataSetChanged();
                    if (mListener != null) mListener.onValueChanged(formElement);
                }
                clickedPosition = -1;
            }
        }

        @Override
        public void onCancelled() {

        }
    };

}